﻿#region Using directives

using System;
using System.Collections.Generic;
using System.Text;

using Weazel.Gezel.Model;
using Weazel.Gezel.Model.Errors;

#endregion

namespace Weazel.Gezel.Parser
{
  public static class ParseSequencer
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.Sequencer
    );

    public static void Parse(Set<Token> follow, Tokenizer t, Model.Model parent)
    {
      ParserDebug.WriteLine(
        "Entering 'ParseSequencer'",
        ConsoleColor.Red
      );

      Set<Token> current = new Set<Token>(
        // Token.Sequencer,
        Token.Identifier,
        Token.LeftParenthesis,
        Token.RightParenthesis,
        Token.LeftBrace,
        Token.RightBrace
      );
		
			Position startPosition = t.CurrentStartPosition;

      t.Accept(Token.Sequencer, follow.Union(current));

      string name = (t.CurrentToken == Token.Identifier) ? t.CurrentString : null;

      Controller controller = new Controller(parent, name, startPosition, null);
      controller.Type = Controller.ControllerType.Sequencer;

      // do not remove 'identifier' from set current (used later)
      t.Accept(Token.Identifier, follow.Union(current));

      current.Remove(Token.LeftParenthesis);
      t.Accept(Token.LeftParenthesis, follow.Union(current));

      controller.DatapathRef = t.CurrentToken == Token.Identifier ? t.CurrentString : null;

      current.Remove(Token.Identifier);
      t.Accept(Token.Identifier, follow.Union(current));

      current.Remove(Token.RightParenthesis);
      t.Accept(Token.RightParenthesis, follow.Union(current));

      current.Remove(Token.LeftBrace);
      t.Accept(Token.LeftBrace, follow.Union(current));

      current.Remove(ParseSequenceDefinition.First);
      ParseSequenceDefinition.Parse(follow.Union(current), t, controller);

			controller.EndPosition = t.CurrentEndPosition;

      current.Remove(Token.RightBrace);
      t.Accept(Token.RightBrace, follow.Union(current));

      if (name != null && controller.DatapathRef != null)
      {
        if (parent.Controllers.ContainsKey(name))
        {
          t.Errors.Add(
            new DuplicateControllerNameParseError(
              controller,
              parent.Controllers[name]
            )
          );
        }
        else
        {
          parent.Controllers.Add(name, controller);
        }
      }

      ParserDebug.WriteLine(
        "Leaving 'ParseSequencer'",
        ConsoleColor.Yellow
      );
    }
  }

  public static class ParseSequenceDefinition
  {
    public static readonly Set<Token> First = new Set<Token>();

    public static void Parse(Set<Token> follow, Tokenizer t, Controller controller)
    {
      ParserDebug.WriteLine(
        "Entering 'ParseSequenceDefinition'",
        ConsoleColor.Red
      );

      Set<Token> current = new Set<Token>(
        Token.Semicolon,
        Token.Identifier
      );

      current = current.Union(ParseCompositeSequenceElement.First);

      bool done = false;

      do
      {
        t.Check(follow.Union(current));

        if (t.CurrentToken == Token.Identifier)
        {          
          controller.SignalFlowGraphRefs.Add(t.CurrentString);
          t.Accept(Token.Identifier, follow.Union(current));
        }
        else if (ParseCompositeSequenceElement.First.Contains(t.CurrentToken))
        {
          controller.SignalFlowGraphRefs.AddRange( ParseCompositeSequenceElement.Parse(follow.Union(current), t).ToArray() );
        }

        t.Check(follow.Union(current));

        if (t.CurrentToken == Token.Semicolon)
        {
          t.Accept(Token.Semicolon, follow.Union(current));
        }
        else
        {
          done = true;
        }

      } while (!done);

      ParserDebug.WriteLine(
        "Leaving 'ParseSequenceDefinition'",
        ConsoleColor.Yellow
      );
    }
  }


  public static class ParseCompositeSequenceElement
  {
    public static readonly Set<Token> First = new Set<Token>(
      Token.LeftParenthesis
    );

    public static List<string> Parse(Set<Token> follow, Tokenizer t)
    {
      ParserDebug.WriteLine(
        "Entering 'ParseCompositeSequenceElement'",
        ConsoleColor.Red
      );

      Set<Token> current = new Set<Token>(
        // Token.LeftParenthesis,
        Token.RightParenthesis
      );

      current = current.Union(ParseList.First);

      t.Accept(Token.LeftParenthesis, follow.Union(current));
      List<CompoundPosition> positions = new List<CompoundPosition>();
      List<string> result = ParseList.Parse(follow.Union(current), t, ref positions);

      t.Accept(Token.RightParenthesis, follow);

      ParserDebug.WriteLine(
        "Leaving 'ParseCompositeSequenceElement'",
        ConsoleColor.Yellow
      );

      return result;
    }
  }
}
